/*
*	Everything in this project uses Queue data structure.. 
*	This file contains all of queue related fuctionalities.
*	Code for Enqueue & Dequeue function taken from internet.
*
*	Author : Shyam (shyam2347@gmail.com)
*	Date : Feb 14, 2012 - Valentines Day :)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct _ins_count
{
	int rmax;
	int wmax;
	int memcount;
};
typedef struct _ins_count ins_count;

struct instruction
{
	char address[20]; // Get it during fetch stage
	char mnemonic[10]; // I should get this during instruction decode stage..
	char operand1[40]; // I should get this during instruction decode stage..
	char operand2[40]; // I should get this during instruction decode stage..
	char operand3[40]; // I should get this during instruction decode stage..
	char operand4[40]; // I should get this during instruction decode stage..
	char operand5[40]; // I should get this during instruction decode stage..
	char s_instruction[70]; // I should get this during instruction fetch stage..
	char creg1[5];
	int reg1; // 0 - nothing, 1 - read, 2 - write
	char creg2[5];
	int reg2;
	char creg3[5];
	int reg3;
	char creg4[5];
	int reg4;
	char creg5[5];
	int reg5;
	char creg6[5];
	int reg6;
	char creg7[5];
	int reg7;
	char creg8[5];
	int reg8;
	int add_op1;
	int add_op2;
	int mul_op1;
	int mul_op2;
	int a_load; //after load, used for load and store instruction
	int add2; // 0 - not done yet, 1 - adder to add buffer.. This is for instruction which has both load and store..
	int load_store; // 0 - nothing, 1 - load, 2 - store, 3 - if it has both load and store..
	int ins_b;
	int ins_type; // This is the type of instruction.. eg., adder, multiplier, divider..
	int rob_entry_unique; //should be an unique # in rob
	int ins_ex_complete; // 0 - not completed, 1 - completed
	int ins_exception; // 0 - no exception, 1 - exception
	unsigned long long inscounter;
	ins_count INSC; // get it during fetch stage
	int rw_check; // 0 - nothing, 1 -read, 2 - write
	char rw_address[16];
};
typedef struct instruction ins;

struct _ins_queue
{
	ins INS;
	struct _ins_queue *next;
}*rear,*front, *d_rear, *d_front, *is_front, *is_rear, *rob_front, *rob_rear, *adder1_front, *adder1_rear, *adder2_front, *adder2_rear, *multiplier_front, *multiplier_rear, *divider_front, *divider_rear, *default_front, *default_rear, *load_buffer_front, *load_buffer_rear, *store_buffer_front, *store_buffer_rear, *storep_buffer_front, *storep_buffer_rear;
typedef struct _ins_queue ins_queue;

void init_queue(void)
{
        front,rear = NULL;
        d_front,d_rear = NULL;
        is_front,is_rear = NULL;
        rob_front,rob_rear = NULL;
        adder1_front,adder1_rear = NULL;
        adder2_front,adder2_rear = NULL;
	load_buffer_front, load_buffer_rear = NULL;
	store_buffer_front, store_buffer_rear = NULL;
        multiplier_front,multiplier_rear = NULL;
        divider_front,divider_rear = NULL;
        default_front,default_rear = NULL;
}

int copy_from_queue(ins *instr, ins_queue *q)
{
	strcpy(instr->address,q->INS.address);
	strcpy(instr->mnemonic,q->INS.mnemonic);
	strcpy(instr->s_instruction,q->INS.s_instruction);
	strcpy(instr->operand1, q->INS.operand1);
	strcpy(instr->operand2, q->INS.operand2);
	strcpy(instr->operand3, q->INS.operand3);
	strcpy(instr->operand4, q->INS.operand4);
	strcpy(instr->operand5, q->INS.operand5);
	instr->ins_type = q->INS.ins_type;
	instr->rob_entry_unique = q->INS.rob_entry_unique;
	instr->ins_ex_complete = q->INS.ins_ex_complete;
	instr->ins_exception = q->INS.ins_exception;
	instr->INSC.rmax = q->INS.INSC.rmax;
	instr->INSC.wmax = q->INS.INSC.wmax;
	instr->INSC.memcount = q->INS.INSC.memcount;
	instr->ins_b = q->INS.ins_b;
	instr->inscounter = q->INS.inscounter;
	instr->load_store = q->INS.load_store;
	strcpy(instr->creg1, q->INS.creg1);
	strcpy(instr->creg2, q->INS.creg2);
	strcpy(instr->creg3, q->INS.creg3);
	strcpy(instr->creg4, q->INS.creg4);
	strcpy(instr->creg5, q->INS.creg5);
	strcpy(instr->creg6, q->INS.creg6);
	strcpy(instr->creg7, q->INS.creg7);
	strcpy(instr->creg8, q->INS.creg8);
	instr->reg1 = q->INS.reg1;
	instr->reg2 = q->INS.reg2;
	instr->reg3 = q->INS.reg3;
	instr->reg4 = q->INS.reg4;
	instr->reg5 = q->INS.reg5;
	instr->reg6 = q->INS.reg6;
	instr->reg7 = q->INS.reg7;
	instr->reg8 = q->INS.reg8;
	instr->add2 = q->INS.add2;
	instr->add_op1 = q->INS.add_op1;
	instr->add_op2 = q->INS.add_op2;
	instr->mul_op1 = q->INS.mul_op1;
	instr->mul_op2 = q->INS.mul_op2;
	instr->a_load = q->INS.a_load;
	instr->rw_check = q->INS.rw_check;
	strncpy(instr->rw_address,q->INS.rw_address,15);
	instr->rw_address[15]='\0';
}

int uppercase( char *sPtr )
{
    while( *sPtr != '\0' )
    {
        *sPtr = toupper( ( unsigned char ) *sPtr );
        *sPtr++;
    }
    *sPtr = '\0';
}

int getInsType(char mnem[])
{
        char *tmp = malloc( sizeof(char) * (strlen(mnem) + 1));
        strcpy(tmp,mnem);
        uppercase(tmp);
        //Check and add other instructions too here..
        if( (strcmp(tmp,"NOP") == 0) || (strcmp(tmp,"ADD") == 0) || (strcmp(tmp,"ADC") == 0) || (strcmp(tmp,"SUB") == 0) || (strcmp(tmp,"SBB") == 0) || (strcmp(tmp,"INC") == 0) || (strcmp(tmp,"DEC") == 0) || (strcmp(tmp,"AND") == 0) || (strcmp(tmp,"OR") == 0) || (strcmp(tmp,"XOR") == 0) || (strcmp(tmp,"TEST") == 0) || (strcmp(tmp,"CMP") == 0) || (strcmp(tmp,"NEG") == 0) || (strcmp(tmp,"NOT") == 0) || (strcmp(tmp,"JMP") == 0) || (strcmp(tmp,"CALL") == 0) || (strcmp(tmp,"JB") == 0) || (strcmp(tmp,"LOOPW") == 0) || (strcmp(tmp,"LOOPD") == 0) || (strcmp(tmp,"RET") == 0) || (strcmp(tmp,"RETN") == 0) || (strcmp(tmp,"RETF") == 0) )
        {
		free(tmp);
		PROFILE_ADDER++;
                return FU_ADDER;
        }
        else if( (strcmp(tmp,"DIV") == 0) || (strcmp(tmp,"IDIV") == 0) || (strcmp(tmp,"FDIV") == 0) )
        {
		free(tmp);
		PROFILE_DIVIDER++;
                return FU_DIVIDER;
        }
        else if( (strcmp(tmp,"MUL") == 0) || (strcmp(tmp,"IMUL") == 0) || (strcmp(tmp,"FMUL") == 0) )
        {
		free(tmp);
		PROFILE_MULTIPLIER++;
                return FU_MULTIPLIER;
        }
        else if( (strcmp(tmp, "LAHF") == 0) || (strcmp(tmp,"MOVZX") == 0) || (strcmp(tmp,"MOVSD") == 0) || (strcmp(tmp,"CMOVZ") == 0) || (strcmp(tmp,"MOV") == 0) || (strcmp(tmp,"XCHG") == 0) || (strcmp(tmp,"PUSH") == 0) || (strcmp(tmp,"POP") == 0) || (strcmp(tmp,"PUSHF") == 0) || (strcmp(tmp,"POPF") == 0) || (strcmp(tmp,"LEA") == 0) || (strcmp(tmp,"LDS") == 0) || (strcmp(tmp,"LES") == 0) || (strcmp(tmp,"LFS") == 0) || (strcmp(tmp,"LGS") == 0) || (strcmp(tmp,"LSS") == 0) || (strcmp(tmp,"FLD") == 0) || (strcmp(tmp,"FST") == 0) )
        {
		free(tmp);
		PROFILE_LOADSTORE++;
                return FU_LOADSTORE;
        }
        else if( (strcmp(tmp,"SHR") == 0) || (strcmp(tmp,"SHL") == 0) || (strcmp(tmp,"SAR") == 0) || (strcmp(tmp,"SAL") == 0) || (strcmp(tmp,"ROR") == 0) || (strcmp(tmp,"ROL") == 0) || (strcmp(tmp,"SHLD") == 0) || (strcmp(tmp,"SHRD") == 0) || (strcmp(tmp,"RCR") == 0) || (strcmp(tmp,"RCL") == 0) || (strcmp(tmp,"BT") == 0) || (strcmp(tmp,"BTR") == 0) || (strcmp(tmp,"BTC") == 0) || (strcmp(tmp,"BTS") == 0) || (strcmp(tmp,"BSF") == 0) || (strcmp(tmp,"BSR") == 0) )
        {
		free(tmp);
		PROFILE_LOGICAL++;
                return FU_LOGICAL;
        }
	else
		PROFILE_DEFAULT++;
        free(tmp);
        //printf("Tmp : %s\n",tmp); //At some point in the future, I would like to analyze which instructions goes into default..
        return -1; // I will assume that this is the default path..
}

int getInsBranch(char mnem[])
{
        char *tmp = malloc (sizeof(char) * (strlen(mnem) + 1));
        strcpy(tmp,mnem);
        uppercase(tmp);
	if ((strcmp(tmp,"JMP") == 0) || (strcmp(tmp,"CALL") == 0) || (strcmp(tmp,"JB") == 0) || (strcmp(tmp,"LOOPW") == 0) || (strcmp(tmp,"LOOPD") == 0) || 
		(strcmp(tmp,"RET") == 0) || (strcmp(tmp,"RETN") == 0) || (strcmp(tmp,"RETF") == 0) )
	{
		free(tmp);
		return 1;
	}
	free(tmp);
	return 0;
}

ins is_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(is_front==NULL||is_rear==NULL) exit(0);
        else
        {
                p=is_front;
		copy_from_queue(&instr,p);
	}
	return(instr);
}

//I want to assign a rob_entry no. So, this function should basically return a # such that no other rob ins has that no. as its rob entry.
//Ok, so here is the logic..
//ROB_PREV_NO initial value is 0.. So, I keep incrementing it and assign the old value to each instruction.. this is based on the fact that rob entry commits in order.
int assign_robno()
{
    if(ROB_PREV_NO == ROB_BUFFERLIMIT)
	ROB_PREV_NO = 0;
    return ROB_PREV_NO++;
}
int rob_lastins_complete(void)
{
	if(rob_front == NULL || rob_rear == NULL) exit(0);
	return rob_front->INS.ins_ex_complete;
}
int rob_lastins_exception(void)
{
	if(rob_front == NULL || rob_rear == NULL) exit(0);
	return rob_front->INS.ins_exception;
}
int rob_lastins_loadstore(void)
{
	if(rob_front == NULL || rob_rear == NULL) exit(0);
	return rob_front->INS.load_store;
}
ins rob_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(rob_front==NULL||rob_rear==NULL) exit(0);
        else
        {
                p=rob_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
void rob_setlastinsincomplete(void)
{
	if(rob_front == NULL || rob_rear == NULL) exit(0);
	rob_front->INS.ins_ex_complete = 0;
}

void rob_display(void)
{
    struct _ins_queue *t;
    t = rob_front;
    while (rob_front == NULL || rob_rear == NULL) exit(0);
    while (t != NULL)
    {
        printf("->%s\t->%s\t->%d\t->%d", t->INS.address, t->INS.mnemonic, t->INS.rob_entry_unique, t->INS.ins_type);
        t = t->next;
    }
}
int ins_complete(int rob_entry)
{
    struct _ins_queue *t;
    t = rob_front;
    while (rob_front == NULL || rob_rear == NULL) exit(0);
    while (t != NULL)
    {
	if(t->INS.rob_entry_unique == rob_entry)
	{
		t->INS.ins_ex_complete = 1;
		return 1;
	}
	
        t = t->next;
    }
    return 0;
}
void rob_flush(void)
{
    while(rob_flush_sub() == 1);
}
int rob_flush_sub(void)
{
        struct _ins_queue *p;
        if(rob_front==NULL||rob_rear==NULL) return 0;
        else
        {
                p=rob_front;
                rob_front=rob_front->next;
                free(p);
	}
	return 1;
}

ins adder1_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(adder1_front==NULL||adder1_rear==NULL) exit(0);
        else
        {
                p=adder1_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
ins adder2_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(adder2_front==NULL||adder2_rear==NULL) exit(0);
        else
        {
                p=adder2_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
ins multiplier_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(multiplier_front==NULL||multiplier_rear==NULL) exit(0);
        else
        {
                p=multiplier_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
ins divider_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(divider_front==NULL||divider_rear==NULL) exit(0);
        else
        {
                p=divider_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
ins default_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(default_front==NULL||default_rear==NULL) exit(0);
        else
        {
                p=default_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
ins load_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(load_buffer_front==NULL||load_buffer_rear==NULL) exit(0);
        else
        {
                p=load_buffer_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
void storep_decaddop1(void)
{
        if(storep_buffer_front == NULL || storep_buffer_rear == NULL) exit(0);
        storep_buffer_front->INS.add_op1 -= 1;
}
void load_decaddop2(void)
{
        if(load_buffer_front == NULL || load_buffer_rear == NULL) exit(0);
        load_buffer_front->INS.add_op2 -= 1;
}
void storep_decmulop1(void)
{
        if(storep_buffer_front == NULL || storep_buffer_rear == NULL) exit(0);
        storep_buffer_front->INS.mul_op1 -= 1;
}
void load_decmulop2(void)
{
        if(load_buffer_front == NULL || load_buffer_rear == NULL) exit(0);
        load_buffer_front->INS.mul_op2 -= 1;
}

ins storepre_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(storep_buffer_front==NULL||storep_buffer_rear==NULL) exit(0);
        else
        {
                p=storep_buffer_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}
int storep_setaload(unsigned long long ins_counter)
{
    struct _ins_queue *t;
    t = storep_buffer_front;
    while (storep_buffer_front == NULL || storep_buffer_rear == NULL) exit(0);
    while (t != NULL)
    {
        if(t->INS.inscounter == ins_counter)
        {
                t->INS.a_load = 1;
                return 1;
        }

        t = t->next;
    }
    return 0;
}

ins store_getlastins(void)
{
        struct _ins_queue *p;
        ins instr;
        if(store_buffer_front==NULL||store_buffer_rear==NULL) exit(0);
        else
        {
                p=store_buffer_front;
                copy_from_queue(&instr,p);
        }
        return(instr);
}


int reverse_enqueue(ins instr,int choose)
{
	struct _ins_queue *p;
	p = (struct _ins_queue*) malloc( sizeof(struct _ins_queue) );
        p->INS = instr;
        p->next = NULL;
        if(choose == ADDER1_QUEUE)
	{
		if(adder1_rear==NULL||adder1_front==NULL) 
			adder1_front=p;
        	else 
		{
			p->next = adder1_front;
			adder1_front = p;
		}
	}
        else if(choose == ADDER2_QUEUE)
	{
		if(adder2_rear==NULL||adder2_front==NULL) 
			adder2_front=p;
        	else 
		{
			p->next = adder2_front;
			adder2_front = p;
		}
	}
	return 0;
}

void enqueue_logic(struct _ins_queue *,int);
void dequeue_logic(struct _ins_queue **, int);

int my_enqueue(ins instr,int choose)
{
	struct _ins_queue *p;
	p = (struct _ins_queue*) malloc( sizeof(struct _ins_queue) );
	if(choose == ID_QUEUE)
	{
		instr.ins_type = getInsType(instr.mnemonic);
		instr.ins_b = getInsBranch(instr.mnemonic);
	}
	if(choose == ROB_QUEUE) instr.rob_entry_unique = assign_robno();
	p->INS = instr;
	p->next = NULL;
	enqueue_logic(p,choose);
	if(choose == ROB_QUEUE) return p->INS.rob_entry_unique; // Returning this, so that its useful during execute phase in tomasulo..
	return 0;
}
void enqueue_logic(struct _ins_queue *p, int choose)
{
	if(choose == IF_QUEUE)
	{
		if(rear==NULL||front==NULL) front=p;
        	else rear->next=p;
        	rear=p;
	}
	if(choose == ID_QUEUE)
	{
		if(d_rear==NULL||d_front==NULL) d_front=p;
        	else d_rear->next=p;
        	d_rear=p;
	}
	if(choose == IS_QUEUE)
	{
		if(is_rear==NULL||is_front==NULL) is_front=p;
        	else is_rear->next=p;
        	is_rear=p;
	}
	if(choose == ROB_QUEUE)
	{
		if(rob_rear==NULL||rob_front==NULL) rob_front=p;
        	else rob_rear->next=p;
        	rob_rear=p;
	}
	if(choose == ADDER1_QUEUE)
	{
		if(adder1_rear==NULL||adder1_front==NULL) adder1_front=p;
        	else adder1_rear->next=p;
        	adder1_rear=p;
	}
	if(choose == ADDER2_QUEUE)
	{
		if(adder2_rear==NULL||adder2_front==NULL) adder2_front=p;
        	else adder2_rear->next=p;
        	adder2_rear=p;
	}
	if(choose == MULTIPLIER_QUEUE)
	{
		if(multiplier_rear==NULL||multiplier_front==NULL) multiplier_front=p;
        	else multiplier_rear->next=p;
        	multiplier_rear=p;
	}
	if(choose == DIVIDER_QUEUE)
	{
		if(divider_rear==NULL||divider_front==NULL) divider_front=p;
        	else divider_rear->next=p;
        	divider_rear=p;
	}
	if(choose == DEFAULT_QUEUE)
	{
		if(default_rear==NULL||default_front==NULL) default_front=p;
        	else default_rear->next=p;
        	default_rear=p;
	}
	if(choose == LOADER_QUEUE)
	{
		if(load_buffer_rear == NULL || load_buffer_front == NULL) load_buffer_front = p;
		else load_buffer_rear->next=p;
		load_buffer_rear = p;
	}
	if(choose == STORE_QUEUE)
	{
		if(store_buffer_rear == NULL || store_buffer_front == NULL) store_buffer_front = p;
		else store_buffer_rear->next=p;
		store_buffer_rear = p;
	}
	if(choose == STORE_PREQUEUE)
	{
		if(storep_buffer_rear == NULL || storep_buffer_front == NULL) storep_buffer_front = p;
		else storep_buffer_rear->next=p;
		storep_buffer_rear = p;
	}
}


ins my_dequeue(int choose)
{
        struct _ins_queue *p;
        ins instr;
        dequeue_logic(&p,choose);
        copy_from_queue(&instr,p);
        free(p);
        return instr;
}
void dequeue_logic(struct _ins_queue **p, int choose)
{
        if(choose == IF_QUEUE)
        {
                if(front == NULL || rear == NULL) exit(1);
                *p=front;
                front = front->next;
        }
        if(choose == ID_QUEUE)
        {
                if(d_front == NULL || d_rear == NULL) exit(1);
                *p=d_front;
                d_front = d_front->next;
        }
        if(choose == IS_QUEUE)
        {
                if(is_front == NULL || is_rear == NULL) exit(1);
                *p=is_front;
                is_front = is_front->next;
        }
        if(choose == ROB_QUEUE)
        {
                if(rob_front == NULL || rob_rear == NULL) exit(1);
                *p=rob_front;
                rob_front = rob_front->next;
        }
        if(choose == ADDER1_QUEUE)
        {
                if(adder1_front == NULL || adder1_rear == NULL) exit(1);
                *p=adder1_front;
                adder1_front = adder1_front->next;
        }
        if(choose == ADDER2_QUEUE)
        {
                if(adder2_front == NULL || adder2_rear == NULL) exit(1);
                *p=adder2_front;
                adder2_front = adder2_front->next;
        }
        if(choose == MULTIPLIER_QUEUE)
        {
                if(multiplier_front == NULL || multiplier_rear == NULL) exit(1);
                *p=multiplier_front;
                multiplier_front = multiplier_front->next;
        }
        if(choose == DIVIDER_QUEUE)
        {
                if(divider_front == NULL || divider_rear == NULL) exit(1);
                *p=divider_front;
                divider_front = divider_front->next;
        }
        if(choose == DEFAULT_QUEUE)
        {
                if(default_front == NULL || default_rear == NULL) exit(1);
                *p=default_front;
                default_front = default_front->next;
        }
	if(choose == LOADER_QUEUE)
	{
                if(load_buffer_front == NULL || load_buffer_rear == NULL) exit(1);
                *p=load_buffer_front;
                load_buffer_front = load_buffer_front->next;
	}
	if(choose == STORE_QUEUE)
	{
                if(store_buffer_front == NULL || store_buffer_rear == NULL) exit(1);
                *p=store_buffer_front;
                store_buffer_front = store_buffer_front->next;
	}
	if(choose == STORE_PREQUEUE)
	{
                if(storep_buffer_front == NULL || storep_buffer_rear == NULL) exit(1);
                *p=storep_buffer_front;
                storep_buffer_front = storep_buffer_front->next;
	}
}       
